/*
The keyboard.c file implements the lowest level keyboard handler
*/


#include <globals.h>
#include <interrupts.h>
#include <panic.h>
#include <klib.h>
#include <spinlock.h>

//Private Includes
#include <scancodes.h>
#include <buffer.h>


/* Redefine EXTERN because we'll be defining functions for real here */ 


/* Data structures */

unsigned char keymap_us[256] =
{
    0,  27, '1', '2', '3', '4', '5', '6', '7', '8',	/* 9 */
  '9', '0', '-', '=', '\b',	                        /* Backspace */
  '\t',			                                /* Tab */
  'q', 'w', 'e', 'r',	                                /* 19 */
  't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',	        /* Enter key */
    0,			                                /* 29   - Control */
  'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',	/* 39 */
 '\'', '`',   0,		                        /* Left shift */
 '\\', 'z', 'x', 'c', 'v', 'b', 'n',			/* 49 */
  'm', ',', '.', '/',   0,				/* Right shift */
  '*',
    0,	/* Alt */
  ' ',	/* Space bar */
    0,	/* Caps lock */
    0,	/* 59 - F1 key ... > */
    0,   0,   0,   0,   0,   0,   0,   0,
    0,	/* < ... F10 */
    0,	/* 69 - Num lock*/
    0,	/* Scroll Lock */
    0,	/* Home key */
    0,	/* Up Arrow */
    0,	/* Page Up */
  '-',
    0,	/* Left Arrow */
    0,
    0,	/* Right Arrow */
  '+',
    0,	/* 79 - End key*/
    0,	/* Down Arrow */
    0,	/* Page Down */
    0,	/* Insert Key */
    0,	/* Delete Key */
    0,   0,   0,
    0,	/* F11 Key */
    0,	/* F12 Key */
    0,	/* All other keys are undefined */

    /* SHIFT PRESSED */

    0,  27, '!', '@', '#', '$', '%', '^', '&', '*',	/* 9 */
  '(', ')', '_', '+', '\b',	                        /* Backspace */
  '\t',			                                /* Tab */
  'Q', 'W', 'E', 'R',	                                /* 19 */
  'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',	        /* Enter key */
    0,			                                /* 29   - Control */
  'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':',	/* 39 */
 '\"', '~',   0,		                        /* Left shift */
 '|', 'Z', 'X', 'C', 'V', 'B', 'N',			/* 49 */
  'M', '<', '>', '?',   0,				/* Right shift */
    0,
    0,	/* Alt */
  ' ',	/* Space bar */
    0,	/* Caps lock */
    0,	/* 59 - F1 key ... > */
    0,   0,   0,   0,   0,   0,   0,   0,
    0,	/* < ... F10 */
    0,	/* 69 - Num lock*/
    0,	/* Scroll Lock */
    0,	/* Home key */
    0,	/* Up Arrow */
    0,	/* Page Up */
  '-',
    0,	/* Left Arrow */
    0,
    0,	/* Right Arrow */
  '+',
    0,	/* 79 - End key*/
    0,	/* Down Arrow */
    0,	/* Page Down */
    0,	/* Insert Key */
    0,	/* Delete Key */
    0,   0,   0,
    0,	/* F11 Key */
    0,	/* F12 Key */
    0,	/* All other keys are undefined */
};		

int left_shift,right_shift,shift;
int left_ctrl,right_ctrl,ctrl;
int left_alt,right_alt,alt;
int caps_lock;

// Define mutex
spinlock_t mutex;


/* Private functions prototypes */

extern word_t read_kbd(void);
extern word_t read_kbd_status(void);
extern word_t read_kbd_once(void);
extern void kbd_int(void);
int scancode_enqueue(word_t code);
int scancode_dequeue(word_t *code);


void keyboard_isr(void);

/* Functions implementation */



void keyboard_isr(void)
  {
    word_t code;

    /*
    while (read_kbd_status())
      {
	code=read_kbd();
	//printf("Keyboard Interrupt fired, code: 0x%x\n",code);
	printf("KBD: %x\n",code);
      }
    */
    code=read_kbd();
    //printf("KBD: %02x\n",code);
    scancode_enqueue(code);
  }


// enqueue a scancode in the buffer
int scancode_enqueue(word_t code)
  {
    int ret=1;

    /*
      no need to disable interrupts here, 
      becayse cause we're in an ISR,
      hence interrupts are always off.
    */
    lock_acquire(&mutex);

    ret=buffer_enqueue((int)code);

    lock_release(&mutex);

    return ret;
  }

// dequeue a scancode from the buffer
int scancode_dequeue(word_t *code)
  {
    int ret=1;
    int hwi;

    hwi=lock_acquire_cli(&mutex);

    ret=buffer_dequeue((int *)code);
   
    lock_release_sti(&mutex,hwi);

    return ret;
  }



/* Public Functions implementation */

void kbd_initialize(void)
  {
    buffer_initialize();

    lock_initialize(&mutex);

    // Initialize flags
    left_shift=right_shift=shift=0;
    left_ctrl=right_ctrl=ctrl=0;
    left_alt=right_alt=alt=0;
    caps_lock=0;

    //assign_IRQ(1,(dword_t)&kbd_int);
    assign_IRQ(1,(dword_t)&keyboard_isr);
  }

/*
word_t kbd_read_key(void)
  {
    word_t code;

    if (!scancode_dequeue(&code))
      printf("KBD: %02x\n",code);
    
    return code;
  }
*/

word_t kbd_read_key(void)
  {
    word_t rcode=0;
    dword_t code;
    int key_release,extended,unprintable;
    word_t character;

    unprintable=0;
    extended=0;
    // Skip extended codes (E0)
    do {

      // Warning: busy waiting for now... but should block here
      while (scancode_dequeue(&rcode));

      code=(dword_t)rcode;

      if (code==0xE0)
	extended=1;

    } while (code==0xE0);

    key_release=0;
    // break code?
    if (code & 0x80)
      key_release=1;

    // clear high bit
    code &= 0x7F;

    
    if (extended)
      switch (code)
	{
	case R_ALT:
	  right_alt=1-key_release;
	  unprintable=1;
	  break;

	case R_CTRL:
	  right_ctrl=1-key_release;
	  unprintable=1;
	  break;
	}
    else
      switch (code)
	{
	case L_ALT:
	  left_alt=1-key_release;
	  unprintable=1;
	  break;

	case L_CTRL:
	  left_ctrl=1-key_release;
	  unprintable=1;
	  break;

	case L_SHIFT:
	  left_shift=1-key_release;
	  unprintable=1;
	  break;

	case R_SHIFT:
	  right_shift=1-key_release;
	  unprintable=1;
	  break;

	case CAPS_LOCK:
	  caps_lock=1-key_release;
	  unprintable=1;
	  break;
	}

    // update global flags
    alt = left_alt | right_alt;
    ctrl = left_ctrl | right_ctrl;
    shift = left_shift | right_shift;
    
    // Use shift part of table
    if (shift)
      //code+=128;
      code+=90;

    character=0;

    if (code>=0 && code<=255)
      character=keymap_us[code];

    if (!key_release && !unprintable)
      printf("%c",character);

    return character;
  }

